home *** CD-ROM | disk | FTP | other *** search
/ InterCD 2001 May / may_2001.iso / intercd / root / Multimedia / ^DivX_Article / virtualdub / VirtualDub-source-1_4d / capspill.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  2001-03-20  |  15.1 KB  |  618 lines

  1. //    VirtualDub - Video processing and capture application
  2. //    Copyright (C) 1998-2001 Avery Lee
  3. //
  4. //    This program is free software; you can redistribute it and/or modify
  5. //    it under the terms of the GNU General Public License as published by
  6. //    the Free Software Foundation; either version 2 of the License, or
  7. //    (at your option) any later version.
  8. //
  9. //    This program is distributed in the hope that it will be useful,
  10. //    but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. //    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12. //    GNU General Public License for more details.
  13. //
  14. //    You should have received a copy of the GNU General Public License
  15. //    along with this program; if not, write to the Free Software
  16. //    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  17.  
  18. #include "VirtualDub.h"
  19. #include <stdio.h>
  20. #include <stdlib.h>
  21. #include <string.h>
  22. #include <crtdbg.h>
  23. #include <windows.h>
  24. #include <commctrl.h>
  25.  
  26. #include "resource.h"
  27. #include "list.h"
  28. #include "gui.h"
  29. #include "oshelper.h"
  30. #include "capspill.h"
  31.  
  32. CapSpillDrive::CapSpillDrive() {
  33.     path = NULL;
  34.     threshold = priority = 0;
  35. }
  36.  
  37. CapSpillDrive::~CapSpillDrive() {
  38.     delete path;
  39. }
  40.  
  41. void CapSpillDrive::setPath(char *s) {
  42.     delete path;
  43.     path = s;
  44. }
  45.  
  46. char *CapSpillDrive::makePath(char *buf, const char *fn) const {
  47.     char *t, *s;
  48.  
  49.     s = path;
  50.     t = buf;
  51.     while(*t++ = *s++);
  52.  
  53.     --t;
  54.  
  55.     if (t>buf) {
  56.         if (t[-1] != '\\' && t[-1]!=':')
  57.             *t++ = '\\'; 
  58.     }
  59.  
  60.     strcpy(t, fn);
  61.  
  62.     return buf;
  63. }
  64.  
  65. ///////////////////////////////////////////////////////////////////////////
  66.  
  67. extern HINSTANCE g_hInst;
  68. extern const char g_szError[];
  69.  
  70. static List2<CapSpillDrive> g_spillDrives;
  71.  
  72. extern long    g_lSpillMinSize = 50;
  73. extern long g_lSpillMaxSize = 1900;
  74.  
  75. ///////////////////////////////////////////////////////////////////////////
  76.  
  77. __int64 CapSpillGetFreeSpace() {
  78.     CapSpillDrive *pcsd = g_spillDrives.AtHead();
  79.     CapSpillDrive *pcsd_next;
  80.     __int64 i64Total = 0, i64Free;
  81.  
  82.     while(pcsd_next = pcsd->NextFromHead()) {
  83.  
  84.         // Probably shouldn't continuously query free space on a UNC path.
  85.  
  86.         if (pcsd->path[0] != '\\' || pcsd->path[1] != '\\') {
  87.             i64Free = MyGetDiskFreeSpace(pcsd->path);
  88.  
  89.             if (i64Free != -1)
  90.                 i64Total += i64Free;
  91.         }
  92.  
  93.         pcsd = pcsd_next;
  94.     }
  95.  
  96.     return i64Total;
  97. }
  98.  
  99. CapSpillDrive *CapSpillPickDrive(bool fAudio) {
  100.     // Poll drives, look for the highest priority with the most free space
  101.  
  102.     CapSpillDrive *pcsd = g_spillDrives.AtHead();
  103.     CapSpillDrive *pcsd_next;
  104.     CapSpillDrive *pcsd_best = NULL;
  105.     __int64 i64Free, i64FreeBest = 0;
  106.  
  107.     while(pcsd_next = pcsd->NextFromHead()) {
  108.  
  109.         i64Free = MyGetDiskFreeSpace(pcsd->path);
  110.  
  111.         if ((i64Free>>20) > pcsd->threshold + g_lSpillMinSize &&
  112.             (!pcsd_best || pcsd->priority > pcsd_best->priority
  113.             || (pcsd->priority == pcsd_best->priority && i64Free > i64FreeBest))) {
  114.  
  115.             i64FreeBest = i64Free;
  116.             pcsd_best = pcsd;
  117.         }
  118.  
  119.         pcsd = pcsd_next;
  120.     }
  121.  
  122.     return pcsd_best;
  123. }
  124.  
  125. CapSpillDrive *CapSpillFindDrive(const char *path) {
  126.     CapSpillDrive *pcsd = g_spillDrives.AtHead();
  127.     CapSpillDrive *pcsd_next;
  128.     CapSpillDrive *pcsd_best = NULL;
  129.  
  130.     while(pcsd_next = pcsd->NextFromHead()) {
  131.         const char *s = path, *t = pcsd->path;
  132.         char c, d;
  133.  
  134.         while((c=*s++)==(d=*t++) && c)
  135.             ;
  136.  
  137.         if ((!c && !d) || (c=='/' && !d) || (!c && d=='/'))
  138.             return pcsd;
  139.  
  140.         pcsd = pcsd_next;
  141.     }
  142.  
  143.     return NULL;
  144. }
  145.  
  146. void CapSpillSaveToRegistry() {
  147.     DWORD dwNumDrives = 0;
  148.     CapSpillDrive *pcsd = g_spillDrives.AtHead(), *pcsd_next;
  149.     char szDriveConfig[512];
  150.     char szValueName[32];
  151.  
  152.     while(pcsd_next = (CapSpillDrive *)pcsd->NextFromHead()) {
  153.         sprintf(szValueName, "Drive %d", ++dwNumDrives);
  154.         sprintf(szDriveConfig, "%d,%d,%s", pcsd->priority, pcsd->threshold, pcsd->path);
  155.  
  156.         SetConfigString("Capture\\Spill Drives", szValueName, szDriveConfig);
  157.  
  158.         pcsd = pcsd_next;
  159.     }
  160.     SetConfigDword("Capture\\Spill Drives", "Number", dwNumDrives);
  161.     SetConfigDword("Capture\\Spill Drives", "Minimum file size", g_lSpillMinSize);
  162.     SetConfigDword("Capture\\Spill Drives", "Maximum file size", g_lSpillMaxSize);
  163. }
  164.  
  165. void CapSpillRestoreFromRegistry() {
  166.     DWORD dwNumDrives;
  167.     DWORD dwDrive=1;
  168.     char szValueName[32];
  169.     char szDriveConfig[512];
  170.     CapSpillDrive *pcsd;
  171.  
  172.     QueryConfigDword("Capture\\Spill Drives", "Minimum file size", (DWORD *)&g_lSpillMinSize);
  173.     QueryConfigDword("Capture\\Spill Drives", "Maximum file size", (DWORD *)&g_lSpillMaxSize);
  174.  
  175.     if (!QueryConfigDword("Capture\\Spill Drives", "Number", &dwNumDrives))
  176.         return;
  177.  
  178.     while(pcsd = g_spillDrives.RemoveTail())
  179.         delete pcsd;
  180.  
  181.     while(dwDrive <= dwNumDrives) {
  182.         sprintf(szValueName, "Drive %d", dwDrive);
  183.  
  184.         if (QueryConfigString("Capture\\Spill Drives", szValueName, szDriveConfig, sizeof szDriveConfig)) {
  185.             int pri, thresh, pos;
  186.  
  187.             if (2==sscanf(szDriveConfig, "%d,%d%n", &pri, &thresh, &pos) && szDriveConfig[pos]==',') {
  188.                 pcsd = new CapSpillDrive();
  189.                 char *pszPath = strdup(szDriveConfig+pos+1);
  190.  
  191.                 if (!pcsd || !pszPath) {
  192.                     delete pcsd;
  193.                     freemem(pszPath);
  194.                 } else {
  195.                     pcsd->priority = pri;
  196.                     pcsd->threshold = thresh;
  197.                     pcsd->setPath(pszPath);
  198.                     g_spillDrives.AddTail(pcsd);
  199.                 }
  200.             }
  201.         }
  202.  
  203.         ++dwDrive;
  204.     }
  205. }
  206.  
  207. ///////////////////////////////////////////////////////////////////////////
  208.  
  209. static LRESULT APIENTRY LVWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
  210. static void LVBeginEdit(HWND hwndLV, int index, int subitem);
  211.  
  212. ///////////////////////////////////////////////////////////////////////////
  213.  
  214. bool CapSpillAdd(HWND hwnd, CapSpillDrive *pcsd, bool fAddList) {
  215.     LVITEM lvi;
  216.  
  217.     lvi.mask        = LVIF_TEXT | LVIF_PARAM;
  218.     lvi.iItem        = 0;
  219.     lvi.iSubItem    = 0;
  220.     lvi.pszText        = LPSTR_TEXTCALLBACK;
  221.     lvi.lParam        = (LPARAM)pcsd;
  222.  
  223.     if (-1 == ListView_InsertItem(hwnd, &lvi))
  224.         return false;
  225.  
  226.     if (fAddList)
  227.         g_spillDrives.AddTail(pcsd);
  228.  
  229.     return true;
  230. }
  231.  
  232. BOOL CALLBACK CaptureSpillDlgProc(HWND hdlg, UINT msg, WPARAM wParam, LPARAM lParam) {
  233.     switch(msg) {
  234.     case WM_INITDIALOG:
  235.         {
  236.             HWND hwndItem;
  237.             LVCOLUMN lvc;
  238.             RECT r;
  239.             CapSpillDrive *pcsd, *pcsd_next;
  240.  
  241.             hwndItem = GetDlgItem(hdlg, IDC_SPILL_DRIVES);
  242.             GetClientRect(hwndItem, &r);
  243.  
  244.             lvc.mask    = LVCF_FMT | LVCF_TEXT | LVCF_WIDTH;
  245.             lvc.fmt        = LVCFMT_LEFT;
  246.             lvc.cx        = 50;
  247.             lvc.pszText    = "Priority";
  248.  
  249.             ListView_InsertColumn(hwndItem, 0, &lvc);
  250.  
  251.             lvc.pszText    = "Threshold";
  252.             lvc.cx        = 100;
  253.             ListView_InsertColumn(hwndItem, 1, &lvc);
  254.  
  255.             lvc.pszText    = "Path";
  256.             lvc.cx        = r.right - r.left - 150;
  257.             ListView_InsertColumn(hwndItem, 2, &lvc);
  258.  
  259.             pcsd = (CapSpillDrive *)g_spillDrives.AtHead();
  260.  
  261.             while(pcsd_next = (CapSpillDrive *)pcsd->NextFromHead()) {
  262.                 CapSpillAdd(hwndItem, pcsd, false);
  263.                 pcsd = pcsd_next;
  264.             }
  265.  
  266.             guiSubclassWindow(hwndItem, LVWndProc);
  267.  
  268.             SetDlgItemInt(hdlg, IDC_MIN_SIZE, g_lSpillMinSize, FALSE);
  269.             SetDlgItemInt(hdlg, IDC_MAX_SIZE, g_lSpillMaxSize, FALSE);
  270.         }
  271.         return TRUE;
  272.     case WM_COMMAND:
  273.         switch(LOWORD(wParam)) {
  274.         case IDOK:
  275.             {
  276.                 BOOL fOk;
  277.                 LONG lMin, lMax;
  278.  
  279.                 lMin = GetDlgItemInt(hdlg, IDC_MIN_SIZE, &fOk, FALSE);
  280.                 if (!fOk || lMin > 2048) {
  281.                     SetFocus(GetDlgItem(hdlg, IDC_MIN_SIZE));
  282.                     MessageBox(hdlg, "Minimum size must be between 0 and 2048 megabytes.", g_szError, MB_OK);
  283.                     return TRUE;
  284.                 }
  285.                 lMax = GetDlgItemInt(hdlg, IDC_MAX_SIZE, &fOk, FALSE);
  286.                 if (!fOk || lMax < 50 && lMax > 2048) {
  287.                     SetFocus(GetDlgItem(hdlg, IDC_MAX_SIZE));
  288.                     MessageBox(hdlg, "Maximum size must be between 50 and 2048 megabytes.", g_szError, MB_OK);
  289.                     return TRUE;
  290.                 }
  291.  
  292.                 g_lSpillMinSize = lMin;
  293.                 g_lSpillMaxSize = lMax;
  294.             }
  295.             CapSpillSaveToRegistry();
  296.             EndDialog(hdlg, 0);
  297.             return TRUE;
  298.         case IDC_ADD:
  299.             {
  300.                 CapSpillDrive *pcsd = new CapSpillDrive();
  301.  
  302.                 if (!pcsd)
  303.                     return TRUE;
  304.  
  305.                 pcsd->threshold = 50;
  306.  
  307.                 if (!CapSpillAdd(GetDlgItem(hdlg, IDC_SPILL_DRIVES), pcsd, true))
  308.                     delete pcsd;
  309.             }
  310.             return TRUE;
  311.         case IDC_DEL:
  312.             {
  313.                 HWND hwndLV = GetDlgItem(hdlg, IDC_SPILL_DRIVES);
  314.                 int ind;
  315.  
  316.                 ind = ListView_GetNextItem(hwndLV, -1, MAKELPARAM(LVNI_SELECTED,0));
  317.  
  318.                 if (ind >= 0) {
  319.                     LVITEM lvi;
  320.  
  321.                     lvi.iItem = ind;
  322.                     lvi.iSubItem = 0;
  323.                     lvi.mask = LVIF_PARAM;
  324.  
  325.                     if (ListView_GetItem(hwndLV, &lvi)) {
  326.                         CapSpillDrive *pcsd = (CapSpillDrive *)lvi.lParam;
  327.  
  328.                         pcsd->Remove();
  329.                         delete pcsd;
  330.                     }
  331.  
  332.                     ListView_DeleteItem(hwndLV, ind);
  333.                 }
  334.             }
  335.             return TRUE;
  336.         }
  337.         break;
  338.  
  339.     case WM_NOTIFY:
  340.         if (((NMHDR *)lParam)->idFrom == IDC_SPILL_DRIVES) {
  341.             NMLVDISPINFO *pnldi = (NMLVDISPINFO *)lParam;
  342.  
  343.             if (pnldi->hdr.code == LVN_GETDISPINFO) {
  344.                 CapSpillDrive *pcsd, *pcsd_next = NULL;
  345.                 int i = pnldi->item.iItem;
  346.  
  347.                 pcsd = (CapSpillDrive *)pnldi->item.lParam;
  348.  
  349.                 if (!pcsd) {
  350.                     pnldi->item.pszText[0] = 0;
  351.                     return TRUE;
  352.                 }
  353.  
  354.                 switch(pnldi->item.iSubItem) {
  355.                 case 0:
  356.                     _snprintf(pnldi->item.pszText, pnldi->item.cchTextMax, "%+d", pcsd->priority);
  357.                     break;
  358.                 case 1:
  359.                     _snprintf(pnldi->item.pszText, pnldi->item.cchTextMax, "%ldMb", pcsd->threshold);
  360.                     break;
  361.                 case 2:
  362.                     if (pcsd->path)
  363.                         pnldi->item.pszText = pcsd->path;
  364.                     else
  365.                         pnldi->item.pszText[0] = 0;
  366.                     break;
  367.                 default:
  368.                     pnldi->item.pszText[0] = 0;
  369.                     break;
  370.                 }
  371.             }
  372.         }
  373.         return TRUE;
  374.     }
  375.     return FALSE;
  376. }
  377.  
  378. ///////////////////////////////////////////////////////////////////////////
  379.  
  380. static HWND g_hwndBox;
  381. static HWND g_hwndEdit=NULL;
  382. static HWND g_hwndSpin=NULL;
  383. static CapSpillDrive *g_csdPtr;
  384. static int g_csdItem;
  385. static int g_index;
  386.  
  387. int CALLBACK LVSorter(LPARAM lp1, LPARAM lp2, LPARAM lp) {
  388.     CapSpillDrive *pcsd1 = (CapSpillDrive *)lp1;
  389.     CapSpillDrive *pcsd2 = (CapSpillDrive *)lp2;
  390.  
  391.     if (pcsd1->priority > pcsd2->priority)            return -1;
  392.     else if (pcsd1->priority < pcsd2->priority)        return 1;
  393.     else if (pcsd1->threshold > pcsd2->threshold)    return 1;
  394.     else if (pcsd1->threshold < pcsd2->threshold)    return -1;
  395.     else if (pcsd1->path && pcsd2->path)
  396.         return stricmp(pcsd1->path, pcsd2->path);
  397.     else
  398.         return 0;
  399. }
  400.  
  401. static void LVDestroyEdit(bool write, bool sort) {
  402.     if (g_hwndSpin) {
  403.         DestroyWindow(g_hwndSpin);
  404.         g_hwndSpin = NULL;
  405.     }
  406.  
  407.     if (g_hwndEdit) {
  408.         if (g_csdItem == 2) {
  409.             int len = GetWindowTextLength(g_hwndEdit);
  410.             char *s;
  411.  
  412.             if (s = (char *)allocmem(len+1)) {
  413.                 GetWindowText(g_hwndEdit, s, len+1);
  414.                 g_csdPtr->setPath(s);
  415.             }
  416.         } else {
  417.             char buf[32];
  418.             long lv;
  419.  
  420.             GetWindowText(g_hwndEdit, buf, sizeof buf);
  421.  
  422.             if (1 == sscanf(buf, "%ld", &lv)) {
  423.                 if (!g_csdItem) {
  424.                     if (lv<-128) lv = -128; else if (lv>127) lv=127;
  425.                     g_csdPtr->priority = lv;
  426.                 } else {
  427.                     if (lv<0) lv=0;
  428.                     g_csdPtr->threshold = lv;
  429.                 }
  430.             }
  431.         }
  432.         DestroyWindow(g_hwndEdit);
  433.         g_hwndEdit = NULL;
  434.  
  435.         if (sort) {
  436.             SendMessage(g_hwndBox, LVM_REDRAWITEMS, g_index, g_index);
  437.             SendMessage(g_hwndBox, LVM_SORTITEMS, 0, (LPARAM)&LVSorter);
  438.         }
  439.     }
  440. }
  441.  
  442. static LRESULT APIENTRY LVEditWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
  443.     WNDPROC wpOld = (WNDPROC)GetWindowLong(hwnd, GWL_USERDATA);
  444.  
  445.     switch(msg) {
  446.     case WM_GETDLGCODE:
  447.         return CallWindowProc(wpOld, hwnd, msg, wParam, lParam) | DLGC_WANTALLKEYS;
  448.         break;
  449.     case WM_KEYDOWN:
  450.         if (wParam == VK_UP) {
  451.             if (g_index > 0)
  452.                 LVBeginEdit(g_hwndBox, g_index-1, g_csdItem);
  453.             return 0;
  454.         } else if (wParam == VK_DOWN) {
  455.             if (g_index < SendMessage(g_hwndBox, LVM_GETITEMCOUNT, 0, 0)-1)
  456.                 LVBeginEdit(g_hwndBox, g_index+1, g_csdItem);
  457.             return 0;
  458.         } else if (wParam == VK_TAB) {
  459.             if ((SHORT)GetKeyState(VK_SHIFT) < 0) {
  460.                 if (g_csdItem > 0)
  461.                     LVBeginEdit(g_hwndBox, g_index, g_csdItem-1);
  462.             } else {
  463.                 if (g_csdItem < 2)
  464.                     LVBeginEdit(g_hwndBox, g_index, g_csdItem+1);
  465.             }
  466.             return 0;
  467.         }
  468.         break;
  469.     case WM_CHAR:
  470.         if (wParam == 0x0d) {
  471.             LVDestroyEdit(true, true);
  472.             return 0;
  473.         } else if (wParam == 0x1b) {
  474.             LVDestroyEdit(false, true);
  475.             return 0;
  476.         }
  477.         break;
  478.     case WM_KILLFOCUS:
  479.         LVDestroyEdit(true, true);
  480.         break;
  481.     case WM_ACTIVATE:
  482.         if (LOWORD(wParam) == WA_INACTIVE)
  483.             if (!g_hwndSpin || (HWND)lParam != g_hwndSpin)
  484.                 LVDestroyEdit(true, true);
  485.         break;
  486.     }
  487.     return CallWindowProc(wpOld, hwnd, msg, wParam, lParam);
  488. }
  489.  
  490. static void LVBeginEdit(HWND hwndLV, int index, int subitem) {
  491.     RECT r;
  492.     int w=0, w2=0;
  493.     int i;
  494.  
  495.     for(i=0; i<=subitem; i++) {
  496.         w2 += w = SendMessage(hwndLV, LVM_GETCOLUMNWIDTH, i, 0);
  497.     }
  498.  
  499.     LVDestroyEdit(true, false);
  500.  
  501.     g_index = index;
  502.  
  503.     r.left = LVIR_BOUNDS;
  504.  
  505.     SendMessage(hwndLV, LVM_GETITEMRECT, index, (LPARAM)&r);
  506.  
  507.     g_hwndBox = hwndLV;
  508.     g_hwndEdit = CreateWindow("EDIT",
  509.             NULL,
  510.             WS_VISIBLE|WS_CHILD|WS_BORDER | ES_WANTRETURN|ES_AUTOHSCROLL,
  511.             w2-w - 1,
  512.             r.top - 1,
  513.             w + 2,
  514.             r.bottom-r.top + 2,
  515.             hwndLV, (HMENU)1, g_hInst, NULL);
  516.     
  517.     if (g_hwndEdit) {
  518.         LVITEM lvi;
  519.         CapSpillDrive *pcsd;
  520.         char buf[32];
  521.  
  522.         lvi.iItem = index;
  523.         lvi.iSubItem = 0;
  524.         lvi.mask = LVIF_PARAM;
  525.  
  526.         SendMessage(hwndLV, LVM_GETITEM, 0, (LPARAM)&lvi);
  527.  
  528.         g_csdPtr = pcsd = (CapSpillDrive *)lvi.lParam;
  529.         g_csdItem = subitem;
  530.  
  531.         if (subitem<2)
  532.             g_hwndSpin = CreateUpDownControl(WS_VISIBLE|WS_CHILD|UDS_ALIGNRIGHT|UDS_SETBUDDYINT, 0,0,0,0, hwndLV, 1, g_hInst, g_hwndEdit, 127, -128, 0);
  533.  
  534.         guiSubclassWindow(g_hwndEdit, LVEditWndProc);
  535.         SendMessage(g_hwndEdit, WM_SETFONT, SendMessage(hwndLV, WM_GETFONT, 0, 0), MAKELPARAM(FALSE,0));
  536.  
  537.         switch(subitem) {
  538.         case 0:
  539.             sprintf(buf, "%d", pcsd->priority);
  540.             SetWindowText(g_hwndEdit, buf);
  541.             break;
  542.         case 1:
  543.             sprintf(buf, "%d", pcsd->threshold);
  544.             SetWindowText(g_hwndEdit, buf);
  545.             break;
  546.         case 2:
  547.             if (pcsd->path)
  548.                 SetWindowText(g_hwndEdit, pcsd->path);
  549.             break;
  550.         }
  551.  
  552.         SetFocus(g_hwndEdit);
  553.     }
  554. }
  555.  
  556. static LRESULT APIENTRY LVWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
  557.     WNDPROC wpOld = (WNDPROC)GetWindowLong(hwnd, GWL_USERDATA);
  558.  
  559.     switch(msg) {
  560.     case WM_DESTROY:
  561.         LVDestroyEdit(true, false);
  562.         break;
  563.  
  564.     case WM_GETDLGCODE:
  565.         if (g_hwndEdit)
  566.             return CallWindowProc(wpOld, hwnd, msg, wParam, lParam) | DLGC_WANTALLKEYS;
  567.         else
  568.             return CallWindowProc(wpOld, hwnd, msg, wParam, lParam) | DLGC_DEFPUSHBUTTON;
  569.  
  570.     case WM_CHAR:
  571.         if (wParam == '\r') {
  572.             int index = CallWindowProc(wpOld, hwnd, LVM_GETNEXTITEM, -1, MAKELPARAM(LVNI_ALL|LVNI_SELECTED,0));
  573.  
  574.             if (index>=0) {
  575.                 LVBeginEdit(hwnd, index, 0);
  576.             }
  577.         }
  578.         break;
  579.  
  580.     case WM_LBUTTONDOWN:
  581.         {
  582.             LVHITTESTINFO htinfo;
  583.             LVITEM lvi;
  584.             int index;
  585.  
  586.             // if this isn't done, the control doesn't gain focus properly...
  587.  
  588.             CallWindowProc(wpOld, hwnd, msg, wParam, lParam);
  589.  
  590.             htinfo.pt.x    = 2;
  591.             htinfo.pt.y = HIWORD(lParam);
  592.  
  593.             index = CallWindowProc(wpOld, hwnd, LVM_HITTEST, 0, (LPARAM)&htinfo);
  594.  
  595.             if (index >= 0) {
  596.                 int x = LOWORD(lParam);
  597.                 int w2=0, w;
  598.                 int i=-1;
  599.  
  600.                 lvi.state = lvi.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
  601.                 CallWindowProc(wpOld, hwnd, LVM_SETITEMSTATE, index, (LPARAM)&lvi);
  602.  
  603.                 for(i=0; i<3; i++) {
  604.                     w2 += w = CallWindowProc(wpOld, hwnd, LVM_GETCOLUMNWIDTH, i, 0);
  605.                     if (x<w2) {
  606.                         LVBeginEdit(hwnd, index, i);
  607.  
  608.                         return 0;
  609.                     }
  610.                 }
  611.             }
  612.             LVDestroyEdit(true, false);
  613.         }
  614.         return 0;
  615.     }
  616.     return CallWindowProc(wpOld, hwnd, msg, wParam, lParam);
  617. }
  618.